#did: fixed collision

import direct.directbase.DirectStart
from direct.actor import Actor
from direct.fsm import FSM #importing finite state machine. not needed though.
from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.task import Task #all games require heavy use of tasks
from direct.showbase.DirectObject import DirectObject

import math

#simple creating and showing an environment.2
#environ = loader.loadModel("models/defaults/environment")
#environ=loader.loadModel("models/envir/BasicEnv.egg")
environ=loader.loadModel("/c/Panda3D-1.6.2/AG/models/world.egg")
environ.reparentTo(render)
#environ.setScale(.025)

#giving control of the mouse to user
base.disableMouse()
base.camera.setPos(Point3(0, 20, 7.5))
base.camera.setHpr(0,-30,0)

class Rider (DirectObject):
    def __init__(self):
        
        self.actor = Actor.Actor("/c/Panda3D-1.6.2/AG/models/walking_anim", 
                         {"stand-walk":"/c/Panda3D-1.6.2/AG/models/stand2walkS", #for walking. this may be obsolete
                          "walk":"/c/Panda3D-1.6.2/AG/models/walking_anim", #for walking. i believe this is the proper walking animation
                          "runAT":"/c/Panda3D-1.6.2/AG/models/runningAT"}) #the running animation from stand-run2
    
        self.actor.setScale(.005,.005,.005)
        
        self.actor.reparentTo(render)
        #self.actor.setPlayRate(.7, 'walk')
        self.stand2walkControl=self.actor.getAnimControl("stand-walk")
        
        self.keyMap = {"left":0, "right":0, "up":0, "down":0, "mouse-1":0, "mouse-3":0, "enter":0} #keymap that includes all the keys we expect from input. add new needed keys here

        
        self.accept("arrow_up", self.setKey, ["up", 1])
        self.accept("arrow_down", self.setKey, ["down", 1])
        self.accept("arrow_right", self.setKey, ["right", 1])
        self.accept("arrow_left", self.setKey, ["left", 1])
        self.accept("shift", self.setKey, ["shift", 1])
        self.accept("mouse1", self.setKey, ["mouse-1", 1])
        self.accept("mouse2", self.setKey, ["mouse-2", 1])
        self.accept("mouse3", self.setKey, ["mouse-3", 1])
        
        self.accept("enter", self.pause)

        
        self.accept("arrow_up-up", self.setKey, ["up", 0])
        self.accept("arrow_down-up", self.setKey, ["down", 0])
        self.accept("arrow_right-up", self.setKey, ["right", 0])
        self.accept("arrow_left-up", self.setKey, ["left", 0])
        self.accept("mouse1-up", self.setKey, ["mouse-1", 0])
        self.accept("mouse2-up", self.setKey, ["mouse-2", 0])
        self.accept("mouse3-up", self.setKey, ["mouse-3", 0])
        #self.accept("enter", self.setKey, ["enter", 0])
        
        self.step="R" #to distinguish between right and left foot so we can flip the model instead of making a seperate animation
        self.state="stand" #needed to check which state the char is in so we can move into appropraite next states
        self.startP=0 #honestly i don't even know. XD
        
        self.cTrav = CollisionTraverser()

        self.GroundRay = CollisionRay()
        self.GroundRay.setOrigin(0,0,1000)
        self.GroundRay.setDirection(0,0,-1)
        self.GroundCol = CollisionNode('BillRay')
        self.GroundCol.addSolid(self.GroundRay)
        self.GroundCol.setFromCollideMask(BitMask32.bit(0))
        self.GroundCol.setIntoCollideMask(BitMask32.allOff())
        self.GroundColNp = render.attachNewNode(self.GroundCol)
        self.GroundColNp.setPos(self.actor.getPos())
        self.GroundHandler = CollisionHandlerQueue()
        self.cTrav.addCollider(self.GroundColNp, self.GroundHandler)
        
        self.GroundColNp.show()
        self.cTrav.showCollisions(render)
        
        self.actor.pose("stand-walk", 0) #posed in 1st frame of standing to begin. not sure why. oh, its obsolete
        
        self.speed=0/30 #init speed. its out of 30, because panda runs @ 30 fps
        #stats

        self.maxSpeed=9.0/30.0
        self.accelScale=1.0/21000.0 #how much your accel goes up as you lean more forward
        
        base.win.movePointer(0, base.win.getProperties().getXSize()/2, base.win.getProperties().getYSize()/2)
        self.prevMouseX=0 #needed to determine how far the mouse moved to adjust heading & pitch 
        self.prevMouseY=0
        self.count=0
        self.prevPos=self.actor.getPos()
        self.actor.setH(90+self.actor.getH())
        self.actor.pose("runAT", 1)
        self.pause=False
        self.calf=self.actor.exposeJoint(None, "modelRoot", "Bip01 R Calf", localTransform=True)
        self.calfH=0
        self.calfControl=NodePath()
        taskMgr.add(self.RunAT, "running with ATs")
    
    def setKey(self, key, value):
        self.keyMap[key]=value
    
    def pause(self):
        if (self.keyMap["enter"]!=0):
            self.keyMap["enter"]=0
            base.disableMouse()
            self.pause=False
            
        else:
            self.keyMap["enter"]=1
            mat=Mat4(camera.getMat())
            mat.invertInPlace()
            base.mouseInterfaceNode.setMat(mat)
            base.enableMouse()
            self.pause=True
    '''
    #suggestions: put "stats" in __init__
    apply mouseP to run2 
    make it start large screen
    exit button,
    actually progress lolol 
    put in acceleration
    consider making everything fps and playrate sensitive (that is to say make constants based on fps and playrate)
    shouldn't be able to skip frame 42
    '''
    #main task for running
    
    def RunAT(self, task):
        if (self.pause):
            return Task.cont
        #if char is in stand and user left clicks it will put him in runL1
        if self.keyMap["mouse-1"]!=0 and self.keyMap["mouse-3"]==0 and self.state=="stand": 
            if self.step=="R": #needed to switch between left and right.
                self.actor.setSx(self.actor, -1) #flips model
                self.step="L"
            self.actor.play("runAT", fromFrame=2, toFrame=41) #
            self.speed=self.speed+(1.0/30.0)
            self.state="stand-runL1"
        #add mouseP and adjust the pitch instead of speed
        elif self.state=="stand-runL1":
            self.mouseHR(30, 15, 1.0/60.0) #allows for movement during transition
            self.actor.setP(self.actor.getP()+15.0/126.0)
            #self.speed=self.speed+(1.0/30.0) #slowly accelerates to appropriate speed for run1L
            offSet=10
            minSpeed=0
            self.count+=1
            print self.count
            if self.speed+(self.actor.getP()-offSet)*self.accelScale>=minSpeed and self.speed+(self.actor.getP()-offSet)*self.accelScale<=self.maxSpeed:#would be
                self.speed=self.speed+(self.actor.getP()-offSet)*self.accelScale
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale<minSpeed:
                self.speed=minSpeed
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale>self.maxSpeed:
                self.speed=self.maxSpeed
                
            if self.actor.getCurrentFrame('runAT')>=41: #checks how long char has been in this state so it knows when to change
                self.state="runL1"
        if self.state=="runL1" and self.keyMap["mouse-1"]!=0:
            minSpeed=0.0/30.0
            offSet=10 #how far u lean at neutral
            self.mouseHR(30, 15, 1.0/60.0) #allows for turning
            self.mouseP(45, 0, 70) #allows for leaning/speeding up
            
            #increases speed. if statements are to make sure it stays within min and max
            if self.speed+(self.actor.getP()-offSet)*self.accelScale>=minSpeed and self.speed+(self.actor.getP()-offSet)*self.accelScale<=self.maxSpeed:#would be
                self.speed=self.speed+(self.actor.getP()-offSet)*self.accelScale
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale<minSpeed:
                self.speed=minSpeed
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale>self.maxSpeed:
                self.speed=self.maxSpeed
                
            if self.actor.getCurrentFrame("runAT")!=42: #lol, prob unneeded. =/
                self.actor.pose("runAT", 42)
                print "guess i was wrong"
        if self.state=="runL1" and self.keyMap["mouse-1"]==0: #starts transition
            self.actor.play("runAT", fromFrame=43, toFrame=74)
            #self.startP=self.actor.getP() #tbh, idk
            self.calfH=self.calf.getH()
            self.calfControl=self.actor.controlJoint(None, "modelRoot","Bip01 R Calf")
            self.calfControl.setH(self.calf.getH())
            self.state="runL1-runL2"
        #adjust the pitch from where it is to a neural position (10 degrees?)
        if self.state=="runL1-runL2":
            #self.actor.setP(self.actor.getP()-(self.startP/196.0))
            #takes 185 frames
            self.calfControl.setH(self.calfH-self.actor.getP())

            offSet=0
            minSpeed=0
            if self.speed+(self.actor.getP()-offSet)*self.accelScale>=minSpeed and self.speed+(self.actor.getP()-offSet)*self.accelScale<=self.maxSpeed:#would be
                self.speed=self.speed+(self.actor.getP()-offSet)*self.accelScale
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale<minSpeed:
                self.speed=minSpeed
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale>self.maxSpeed:
                self.speed=self.maxSpeed
            if self.actor.getCurrentFrame("runAT")>=74:
                self.state="runL2"
        if self.state=="runL2":
            minSpeed=0/30
            offSet=0
            self.mouseHR(30, 15, 1.0/60.0)
            self.mouseP(35, 0, 15)
            self.calfControl.setH(self.calfH-self.actor.getP())
            if self.speed+(self.actor.getP()-offSet)*self.accelScale>=minSpeed and self.speed+(self.actor.getP()-offSet)*self.accelScale<=self.maxSpeed:#would be
                self.speed=self.speed+(self.actor.getP()-offSet)*self.accelScale
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale<minSpeed:
                self.speed=minSpeed
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale>self.maxSpeed:
                self.speed=self.maxSpeed
                
        '''
        #need new animation for leg switch
        
        if self.state=="runL2" and self.keyMap["mouse-3"]!=0 and self.keyMap["mouse-1"]==0:
            self.actor.play("runAT")            
            self.state="runL2-runR1"
        if self.state=="runL2-runR1":
            if self.frame+42<=task.frame:
                self.state=runR1
        '''
#####################starts right leg#########################
            
        if self.keyMap["mouse-3"]!=0 and self.keyMap["mouse-1"]==0 and self.state=="stand":
            if self.step=="L":
                self.actor.setSx(self.actor, -1)
                self.step="R"
            self.actor.play("runAT", fromFrame=2, toFrame=41)
            self.frame=task.frame
                                                                                                                                                                                                                                                                                                                                  
            self.speed=self.speed+(.5/30)
            self.state="stand-runR1"
        elif self.state=="stand-runR1":
            self.mouseHR(30, 15, 1.0/60.0)
            self.speed=self.speed+(.5/30)
            if self.frame+39<=task.frame:
                self.state="runR1" 
        if self.state=="runR1" and self.keyMap["mouse-3"]!=0:
            minSpeed=0/30
            offSet=10
            self.mouseHR(30, 15, 1.0/60.0)
            self.mouseP(45, 0, 45)
            
            if self.speed+(self.actor.getP()-offSet)*self.accelScale>=minSpeed and self.speed+(self.actor.getP()-offSet)*self.accelScale<=self.maxSpeed:#would be
                self.speed=self.speed+(self.actor.getP()-offSet)*self.accelScale
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale<minSpeed:
                self.speed=minSpeed
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale>self.maxSpeed:
                self.speed=self.maxSpeed
                
            if self.actor.getCurrentFrame("runAT")!=42:
                self.actor.pose("runAT", 42)
        if self.state=="runR1" and self.keyMap["mouse-3"]==0:
            self.actor.play("runAT", fromFrame=43, toFrame=74)
            self.startP=self.actor.getP()
            self.state="runR1-runR2"
        if self.state=="runR1-runR2":
            self.actor.setP(self.actor.getP()-(self.startP/196.0))
            if self.actor.getCurrentFrame()>=74:
                self.state="runR2"                  
        if self.state=="runR2":
            minSpeed=0/30
            offSet=10
            self.mouseHR(30, 15, 1.0/60.0)
            self.mouseP(35, 0, 30)
            if self.speed+(self.actor.getP()-offSet)*self.accelScale>=minSpeed and self.speed+(self.actor.getP()-offSet)*self.accelScale<=self.maxSpeed:#would be
                self.speed=self.speed+(self.actor.getP()-offSet)*self.accelScale
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale<minSpeed:
                self.speed=minSpeed
            elif self.speed+(self.actor.getP()-offSet)*self.accelScale>self.maxSpeed:
                self.speed=self.maxSpeed
        '''
        #need new animation for leg switch
        
        if self.state=="runR2" and self.keyMap["mouse-1"]!=0:
            self.actor.play("runAT")
            self.state="runR2-runL1"
        if self.state=="runR2-runL1":
            if self.frame+42<=task.frame:
                self.state=runL1
        '''
        
        base.camera.setPos(self.actor.getX()-(20*math.sin((self.actor.getH()*math.pi/180))),
                           self.actor.getY()+(20*math.cos((self.actor.getH()*math.pi/180))),
                           base.camera.getZ())
        base.camera.lookAt(self.actor)
        startpos=self.actor.getPos()
        
        self.move()
        self.GroundColNp.setPos(self.actor.getPos())

        self.cTrav.traverse(render)

        entries = []
        for i in range(self.GroundHandler.getNumEntries()):
            entry = self.GroundHandler.getEntry(i)
            entries.append(entry)
        entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(),
                                     x.getSurfacePoint(render).getZ()))
        if (len(entries)>0) and (entries[0].getIntoNode().getName() == "terrain"):
            self.actor.setZ(entries[0].getSurfacePoint(render).getZ())
        else:
            self.actor.setPos(startpos)
            self.speed=0

        return Task.cont
    
    def move(self):
        x=self.speed*math.sin((self.actor.getH()*math.pi/180))
        y=self.speed*math.cos((self.actor.getH()*math.pi/180))
        self.actor.setPos(self.actor.getX()+x, self.actor.getY()-y, self.actor.getZ())


    def mouseHR(self, limit, scale, scaleH):
        xBorder=.9
        try:
            if base.mouseWatcherNode.getMouseX()>xBorder:
                if (self.actor.getR()+(self.prevMouseX-xBorder)*scale)>=-limit:
                    self.actor.setR(self.actor.getR()+(self.prevMouseX-xBorder)*scale)
                else:
                    self.actor.setR(-limit)
                base.win.movePointer(0,
                                     base.win.getProperties().getXSize()*(1-xBorder)/2,
                                     base.win.getProperties().getYSize()*((-(base.mouseWatcherNode.getMouseY())+1)/2))
                self.prevMouseX=-xBorder
            elif base.mouseWatcherNode.getMouseX()<-xBorder:
                if (self.actor.getR()+(self.prevMouseX-(-xBorder))*scale)<=limit:
                    self.actor.setR(self.actor.getR()+(self.prevMouseX-(-xBorder))*scale)
                else:
                    self.actor.setR(limit)
                base.win.movePointer(0,
                                     base.win.getProperties().getXSize()*(1-((1-xBorder)/2)),
                                     base.win.getProperties().getYSize()*((-(base.mouseWatcherNode.getMouseY())+1)/2))            
                self.prevMouseX=xBorder
            elif self.prevMouseX!=base.mouseWatcherNode.getMouseX():
                if (self.actor.getR()+(self.prevMouseX-base.mouseWatcherNode.getMouseX())*scale)>=-limit and (self.actor.getR()+(self.prevMouseX-base.mouseWatcherNode.getMouseX())*scale)<=limit:
                    self.actor.setR(self.actor.getR()+(self.prevMouseX-base.mouseWatcherNode.getMouseX())*scale)
                elif (self.actor.getR()+(self.prevMouseX-base.mouseWatcherNode.getMouseX())*scale)<-limit:
                    self.actor.setR(-limit)
                elif (self.actor.getR()+(self.prevMouseX-base.mouseWatcherNode.getMouseX())*scale)>limit:
                    self.actor.setR(limit)
                self.prevMouseX=base.mouseWatcherNode.getMouseX()
        except AssertionError:
             self.prevMouseX=0
             self.prevMouseY=0
             base.win.movePointer(0,
                                  base.win.getProperties().getXSize()/2,
                                  base.win.getProperties().getYSize()/2)
        self.actor.setH(self.actor.getH()+(self.actor.getR()*scaleH))#seperate
        
    #mouseP reads the mouse and controls the pitch. limit is how far forward, limitback is how far back. and scale relates how much the mouse moves to how much the pitch changes
    def mouseP(self, limit, limitBack, scale):
        yBorder=.9
        try:
            if base.mouseWatcherNode.getMouseY()>yBorder:
                if (self.actor.getP()+(yBorder-self.prevMouseY)*scale)<=limit:
                    self.actor.setP(self.actor.getP()+(yBorder-self.prevMouseY)*scale)
                else:
                    self.actor.setP(limit)
                base.win.movePointer(0,
                                     base.win.getProperties().getXSize()*((base.mouseWatcherNode.getMouseX()+1)/2),
                                     base.win.getProperties().getYSize()*(1-((1-yBorder)/2)))
                self.prevMouseY=-yBorder
            elif base.mouseWatcherNode.getMouseY()<-yBorder:
                if (self.actor.getP()+(-yBorder-self.prevMouseY)*scale)>=limitBack:
                    self.actor.setP(self.actor.getP()+(-yBorder-self.prevMouseY)*scale)
                else:
                    self.actor.setP(limitBack)
                base.win.movePointer(0,
                                     base.win.getProperties().getXSize()*((base.mouseWatcherNode.getMouseX()+1)/2),
                                     base.win.getProperties().getYSize()*(1-yBorder)/2)
                self.prevMouseY=yBorder
            elif self.prevMouseY!=base.mouseWatcherNode.getMouseY():
                if (self.actor.getP()+(base.mouseWatcherNode.getMouseY()-self.prevMouseY)*scale)<=limit and (self.actor.getP()+(base.mouseWatcherNode.getMouseY()-self.prevMouseY)*scale)>=limitBack:
                    self.actor.setP(self.actor.getP()+(base.mouseWatcherNode.getMouseY()-self.prevMouseY)*scale)
                elif (self.actor.getP()+(base.mouseWatcherNode.getMouseY()-self.prevMouseY)*scale)>limit:
                    self.actor.setP(limit)
                elif (self.actor.getP()+(base.mouseWatcherNode.getMouseY()-self.prevMouseY)*scale)<limitBack:
                    self.actor.setP(limitBack)
                self.prevMouseY=base.mouseWatcherNode.getMouseY()
        except AssertionError:
            self.prevMouseX=0
            self.prevMouseY=0
            base.win.movePointer(0,
                                 base.win.getProperties().getXSize()/2,
                                 base.win.getProperties().getYSize()/2)

 
Rider()

run()
